///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#include <exception>

namespace Core 
{
class _PhiloCommonExport Exception : public std::exception
{
protected:
	long line;
	int number;
	Util::String typeName;
	Util::String description;
	Util::String source;
	Util::String file;
	mutable Util::String fullDesc;

public:
	enum ExceptionCodes 
	{
		ERR_CANNOT_WRITE_TO_FILE,
		ERR_INVALID_STATE,
		ERR_INVALIDPARAMS,
		ERR_RENDERINGAPI_ERROR,
		ERR_DUPLICATE_ITEM,
		ERR_ITEM_NOT_FOUND,
		ERR_FILE_NOT_FOUND,
		ERR_INTERNAL_ERROR,
		ERR_RT_ASSERTION_FAILED, 
		ERR_NOT_IMPLEMENTED
	};


	Exception( int number, const Util::String& description, const Util::String& source );

	Exception( int number, const Util::String& description, const Util::String& source, const char* type, const char* file, long line );

	Exception(const Exception& rhs);

	~Exception() throw() {}

	void operator = (const Exception& rhs);

	virtual const Util::String& getFullDescription(void) const;

	virtual int getNumber(void) const throw();

	virtual const Util::String &getSource() const { return source; }

	virtual const Util::String &getFile() const { return file; }

	virtual long getLine() const { return line; }

	virtual const Util::String &getDescription(void) const { return description; }

	const char* what() const throw() { return getFullDescription().AsCharPtr(); }
    
};


template <int num>
struct ExceptionCodeType
{
	enum { number = num };
};

// Specialised exceptions allowing each to be caught specifically
// backwards-compatible since exception codes still used

class _PhiloCommonExport UnimplementedException : public Exception 
{
public:
	UnimplementedException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "UnimplementedException", inFile, inLine) {}
};
class _PhiloCommonExport FileNotFoundException : public Exception
{
public:
	FileNotFoundException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "FileNotFoundException", inFile, inLine) {}
};
class _PhiloCommonExport IOException : public Exception
{
public:
	IOException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "IOException", inFile, inLine) {}
};
class _PhiloCommonExport InvalidStateException : public Exception
{
public:
	InvalidStateException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "InvalidStateException", inFile, inLine) {}
};
class _PhiloCommonExport InvalidParametersException : public Exception
{
public:
	InvalidParametersException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "InvalidParametersException", inFile, inLine) {}
};
class _PhiloCommonExport ItemIdentityException : public Exception
{
public:
	ItemIdentityException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "ItemIdentityException", inFile, inLine) {}
};
class _PhiloCommonExport InternalErrorException : public Exception
{
public:
	InternalErrorException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "InternalErrorException", inFile, inLine) {}
};
class _PhiloCommonExport RenderingAPIException : public Exception
{
public:
	RenderingAPIException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "RenderingAPIException", inFile, inLine) {}
};
class _PhiloCommonExport RuntimeAssertionException : public Exception
{
public:
	RuntimeAssertionException(int inNumber, const Util::String& inDescription, const Util::String& inSource, const char* inFile, long inLine)
		: Exception(inNumber, inDescription, inSource, "RuntimeAssertionException", inFile, inLine) {}
};


class ExceptionFactory
{
private:
	/// Private constructor, no construction
	ExceptionFactory() {}
public:
	static UnimplementedException create(
		ExceptionCodeType<Exception::ERR_NOT_IMPLEMENTED> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return UnimplementedException(code.number, desc, src, file, line);
	}
	static FileNotFoundException create(
		ExceptionCodeType<Exception::ERR_FILE_NOT_FOUND> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return FileNotFoundException(code.number, desc, src, file, line);
	}
	static IOException create(
		ExceptionCodeType<Exception::ERR_CANNOT_WRITE_TO_FILE> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return IOException(code.number, desc, src, file, line);
	}
	static InvalidStateException create(
		ExceptionCodeType<Exception::ERR_INVALID_STATE> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return InvalidStateException(code.number, desc, src, file, line);
	}
	static InvalidParametersException create(
		ExceptionCodeType<Exception::ERR_INVALIDPARAMS> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return InvalidParametersException(code.number, desc, src, file, line);
	}
	static ItemIdentityException create(
		ExceptionCodeType<Exception::ERR_ITEM_NOT_FOUND> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return ItemIdentityException(code.number, desc, src, file, line);
	}
	static ItemIdentityException create(
		ExceptionCodeType<Exception::ERR_DUPLICATE_ITEM> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return ItemIdentityException(code.number, desc, src, file, line);
	}
	static InternalErrorException create(
		ExceptionCodeType<Exception::ERR_INTERNAL_ERROR> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return InternalErrorException(code.number, desc, src, file, line);
	}
	static RenderingAPIException create(
		ExceptionCodeType<Exception::ERR_RENDERINGAPI_ERROR> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return RenderingAPIException(code.number, desc, src, file, line);
	}
	static RuntimeAssertionException create(
		ExceptionCodeType<Exception::ERR_RT_ASSERTION_FAILED> code, 
		const Util::String& desc, 
		const Util::String& src, const char* file, long line)
	{
		return RuntimeAssertionException(code.number, desc, src, file, line);
	}

};


#ifndef PHILO_EXCEPT
# define PHILO_EXCEPT(num, desc, src) throw Core::ExceptionFactory::create( \
	Core::ExceptionCodeType<num>(), desc, src, __FILE__, __LINE__ );
# endif

}

